package com.glamour.hlcity.component.framework.proxy;


import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import com.glamour.hlcity.common.annotation.Param;
import com.glamour.hlcity.common.annotation.MySqlLockAspect;
import com.glamour.hlcity.common.enums.ErrorCodeEnum;
import com.glamour.hlcity.common.exception.BizException;
import com.glamour.hlcity.common.util.ListUtil;
import com.glamour.hlcity.common.util.StringUtil;

import com.glamour.hlcity.dal.lock.dao.impl.DBLock;

/**
 * 事务加锁切面
 * @author zhaoxing.liuzx
 *
 */
public class MySqlLockProxy {
	private final static Log LOG=LogFactory.getLog(MySqlLockProxy.class);
	private DBLock dBLock;
	public void setdBLock(DBLock dBLock) {
		this.dBLock = dBLock;
	}
    /**
     * 先获得锁，如果获得锁失败，则抛出异常
     * @param point
     */
    public void beforeAdvice(JoinPoint point){ 
    	if(point.getSignature() instanceof MethodSignature){
    		MethodSignature methodSignature=(MethodSignature)point.getSignature();
    		Method method=methodSignature.getMethod();
    		MySqlLockAspect annotation=method.getAnnotation(MySqlLockAspect.class);
    		String logMsg=point.getSignature().getDeclaringType().getName().concat(".").concat(point.getSignature().getName());
    		if(annotation==null){
    			LOG.info("transaction aop:".concat(logMsg).concat("不需要加锁"));
    			return;
    		}
    		String lockName=lockName(point);
    		if(StringUtil.isBlank(lockName)){
    			return;
    		}
    		//获得锁之前先release锁
    		dBLock.releaseLock(lockName);
    		if(dBLock.getLock(lockName, annotation.second())==1){	
    			LOG.info("transaction aop:".concat(logMsg).concat("加锁").concat(lockName).concat("成功"));
    		}else{
    			LOG.info("transaction aop:".concat(logMsg).concat("加锁").concat(lockName).concat("失败"));
    			throw new BizException(ErrorCodeEnum.M020001,logMsg.concat(lockName).concat("锁没有获得， 不执行"));
    		}
    		
    		
    	}else{
    		LOG.error(point.getSignature().getDeclaringType().getName()+"."+point.getSignature().getName()+"不是MethodSignature");
    		throw new BizException(ErrorCodeEnum.DATA_ILLE,"aop代理有误，不是对方法进行的");
    	}
    	
    }
    /**
     * 获得锁名称
     * @param point
     * @return
     */
    private String lockName(JoinPoint point){
    	if(point.getSignature() instanceof MethodSignature){
    		MethodSignature methodSignature=(MethodSignature)point.getSignature();
    		Method method=methodSignature.getMethod();
    		MySqlLockAspect annotation=method.getAnnotation(MySqlLockAspect.class);
    		String logMsg=point.getSignature().getDeclaringType().getName().concat(".").concat(point.getSignature().getName());
    		if(annotation==null){
    			LOG.info("transaction aop:".concat(logMsg).concat("没有MysqlLockAspect注解"));
    			return null;
    		}
    		 // 解析得到参数名数组
            String[] parameterNames;
            StringBuffer sBuffer=new StringBuffer();
            sBuffer.append(annotation.lockName());
			try {
				parameterNames = parseParameterNames(point, (MethodSignature) point.getSignature());
				if(parameterNames==null || parameterNames.length==0){
					return annotation.lockName();
				}
				for (int i = 0, n = parameterNames.length; i < n; i++) {
	                if (hasAspect(annotation, parameterNames[i])) {
	                	continue;
	                }
	                parameterNames[i]=null;
		        }
				Object[]  args = point.getArgs();
		        for (int i = 0, n = parameterNames.length; i < n; i++) {
	                if (StringUtil.isBlank(parameterNames[i])) {
	                	continue;
	                }
	                
	                sBuffer.append(".").append(args[i].toString());
		         }
		    	return sBuffer.toString();
			} catch (NoSuchMethodException e) {
				return sBuffer.toString();
			}
    	}
    	return null;
    }
    
    private Boolean hasAspect(MySqlLockAspect annotation,String param){
    	if(StringUtil.isBlank(param)){
    		return true;
    	}
    	if(annotation.params()!=null && annotation.params().length!=0){
			List<String> params=ListUtil.toList(annotation.params());
			if(params.contains(param)){
				return true;
			}
    	}
    	return false;
    }
    
    
    /**
     * 解析注释得到参数名
     *
     * @param joinPoint
     * @param signature
     * @throws NoSuchMethodException
     */
    private String[] parseParameterNames(JoinPoint joinPoint, MethodSignature signature) throws NoSuchMethodException {
        String[] parameterNames = new String[joinPoint.getArgs().length];
        Annotation[][] annotations = signature.getMethod().getParameterAnnotations();

        if (annotations != null && annotations.length > 0) {
            for (int i = 0, n = annotations.length; i < n; i++) {
                for (Annotation annotation : annotations[i]) {
                    if (annotation instanceof Param) {
                        parameterNames[i] = ((Param) annotation).name();
                    }
                }
            }
        }
        return parameterNames;
    }
      
    public void afterAdvice(JoinPoint point){  
    	if(point.getSignature() instanceof MethodSignature){
    		MethodSignature methodSignature=(MethodSignature)point.getSignature();
    		Method method=methodSignature.getMethod();
    		MySqlLockAspect annotation=method.getAnnotation(MySqlLockAspect.class);
    		String logMsg=point.getSignature().getDeclaringType().getName().concat(".").concat(point.getSignature().getName());
    		if(annotation==null){
    			LOG.info("transaction aop:".concat(logMsg).concat("没有MysqlLockAspect注解"));
    			return;
    		}
    		String lockName=lockName(point);
    		if(StringUtil.isBlank(lockName)){
    			return;
    		}
    		if(dBLock.releaseLock(lockName)==1){	
    			LOG.info("transaction aop:".concat(logMsg).concat("释放锁").concat(lockName).concat("成功"));
    		}
    	}
		
    }  

	public void aroundAdvice(ProceedingJoinPoint point)throws Throwable{  
		try{
			 point.proceed(point.getArgs());   
		}catch(Exception e){
			MethodSignature methodSignature=(MethodSignature)point.getSignature();
    		Method method=methodSignature.getMethod();
    		MySqlLockAspect annotation=method.getAnnotation(MySqlLockAspect.class);
    		String logMsg=point.getSignature().getDeclaringType().getName().concat(".").concat(point.getSignature().getName());
    		if(annotation==null){
    			if(e instanceof BizException){
    				throw e;
    			}
    			throw new BizException(ErrorCodeEnum.M020003,logMsg.concat("执行时抛出异常"),e);
    		}
			
			String lockName=lockName(point);
			if(StringUtil.isBlank(lockName)){
				if(e instanceof BizException){
    				throw e;
    			}
				throw new BizException(ErrorCodeEnum.M020003,logMsg.concat("执行时抛出异常"),e);
			}
			if(dBLock.releaseLock(annotation.lockName())==1){	
    			LOG.info("transaction aop:".concat(logMsg).concat("释放锁").concat(annotation.lockName()).concat("成功"));
    		}else{
    			LOG.info("transaction aop:".concat(logMsg).concat("释放锁").concat(annotation.lockName()).concat("失败"));
    			throw new BizException(ErrorCodeEnum.M020002,logMsg.concat(annotation.lockName()).concat("锁释放失败"),e);
    		}
			throw new BizException(ErrorCodeEnum.M020003,logMsg.concat("执行时抛出异常"),e);
		}

    }  
	

	
	public void throwingAdvice(Throwable throwing)throws Throwable{  
		// TransactionDefinition td = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
//       TransactionStatus ts = transactionManager.getTransaction(td);
//       try {
//         
//           transactionManager.commit(ts);
//       } catch (Exception e) {
//          
//           transactionManager.rollback(ts);
//           throw new RuntimeException(e);
//
//       } 
	}  

}
